﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using StreamingClient.Localization;
using StreamingClient.StreamManagment;

namespace StreamingClient.Player
{
    public partial class PlayerCtrl : UserControl
    {
        public PlayerCtrl()
        {
            InitializeComponent();
        }

        void VideoElement_CurrentStateChanged(object sender, RoutedEventArgs e)
        {
            if (VideoElement.CurrentState == MediaElementState.Playing)
            {
                Overlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                ShowOverlay();
            }
            SetStatus( VideoElement.CurrentState.ToString());
        }

        private HttpWebRequest _request;
        private IServerAccess _serverAccess;
        private VlcTsMediaStreamSource _mss;
        private bool _waitForPlayingAfterOpened;







        public event EventHandler<ErrorEventArgs> Failed;
        public event EventHandler Ended;

        protected bool CanGoBack { get; set; }


        public StreamingSettings StreamingSettings { get; set; }

        public void Start(string src, StreamingSettings settings)
        {

            CanGoBack = true;

            StreamingSettings = settings;

            if (StreamingSettings.ServerType == ServerType.VLC)
            {

                _serverAccess = new VlcAccess(StreamingSettings);
                _serverAccess.StreamingStarted += StreamingStarted;
            }
            else
            {
                _serverAccess = new WmvAccess(StreamingSettings);
                _serverAccess.StreamingStarted += StreamingStartedWMV;
            }

            _serverAccess.CommunicationFailed += StreamingFailed;
            SetStatus("Connecting");
            _serverAccess.StartStreaming(src);
        }

        private void StreamingFailed(object sender, ErrorEventArgs e)
        {
            NotifyError(e.Error, null);
        }

        private void NotifyError(ErrorCode error, Exception exception)
        {
            if (Failed != null)
            {
                Failed(this, new ErrorEventArgs(error, exception));
            }
        }

        private void StreamingStartedWMV(object sender, StreamingEventArgs e)
        {
            SetStatus( "Loading");
            AttachEvents();
            Dispatcher.BeginInvoke(()=>VideoElement.Source = new Uri(e.StreamUri));
        }

        public void CleanUp()
        {
            DetachEvents();
            VideoElement.Stop();
            if (_serverAccess != null)
            {
                _serverAccess.StopStreaming();
            }
        }

        private void StreamingStarted(object sender, StreamingEventArgs e)
        {
            SetStatus("Caching");
            _request = WebRequest.CreateHttp(e.StreamUri);
            _request.BeginGetResponse(RequestStream_Callback, null);
        }


        private void SetStatus(string status)
        {
            Dispatcher.BeginInvoke((() => { Status.Text = Localize.GetString(status); }));
        }

        private void RequestStream_Callback(IAsyncResult asyncResult)
        {
            WebResponse response;

            try
            {
                response = _request.EndGetResponse(asyncResult);
            }
            catch (Exception ex)
            {
                NotifyError(ErrorCode.DownloadStream, ex);
                return;
            }

            _mss = new VlcTsMediaStreamSource(StreamingSettings, response.GetResponseStream());

            Dispatcher.BeginInvoke(() =>
            {
                Status.Text = "Start playing";
                AttachEvents();
                VideoElement.SetSource(_mss);

            });
        }

        private void AttachEvents()
        {
            if (Dispatcher.CheckAccess())
            {
                VideoElement.CurrentStateChanged += new RoutedEventHandler(VideoElement_CurrentStateChanged);
                VideoElement.MediaOpened += new RoutedEventHandler(Player_MediaOpened);
                VideoElement.MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(Player_MediaFailed);
                VideoElement.MediaEnded += new RoutedEventHandler(Player_MediaEnded);
                
            }
            else
            {
                Dispatcher.BeginInvoke(AttachEvents);
            }

        }
        private void DetachEvents()
        {
            if (Dispatcher.CheckAccess())
            {
                VideoElement.CurrentStateChanged -= new RoutedEventHandler(VideoElement_CurrentStateChanged);
                VideoElement.MediaOpened -= new RoutedEventHandler(Player_MediaOpened);
                VideoElement.MediaFailed -= new EventHandler<ExceptionRoutedEventArgs>(Player_MediaFailed);
                VideoElement.MediaEnded -= new RoutedEventHandler(Player_MediaEnded);
            }
            else
            {
                Dispatcher.BeginInvoke(DetachEvents);
            }
        }

        void Player_MediaEnded(object sender, RoutedEventArgs e)
        {
            NotifyEnd();
        }

        private void NotifyEnd()
        {
            if (Ended != null)
            {
                Ended(this, new EventArgs());
            }
        }

        void Player_MediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
            NotifyError(ErrorCode.VideoFailed, null);
        }

        void Player_MediaOpened(object sender, RoutedEventArgs e)
        {
            _waitForPlayingAfterOpened = true;
            //Overlay.Visibility = Visibility.Collapsed;
        }


        private void VideoElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Overlay.Visibility == Visibility.Visible)
            {
                Overlay.Visibility = Visibility.Collapsed;
            }
            else
            {
                ShowOverlay();
            }
        }

        private void ShowOverlay()
        {
            if (_serverAccess != null)
            {
                _serverAccess.UpdatePos(PosCB);
            }
            Overlay.Visibility = Visibility.Visible;
        }

        private void PosCB(double obj)
        {
            Dispatcher.BeginInvoke(() =>
                                                  {
                                                      ProgressVideo.Value = obj;
                                                  });
            
        }


        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            CleanUp();
            NotifyEnd();
        }

        private Aspect _aspect;

        private void AspectRatio_Click(object sender, RoutedEventArgs e)
        {
            _aspect++;
            if (_aspect == Aspect.Max)
            {
                _aspect = 0;
            }

            switch (_aspect)
            {
                case Aspect.FillScreenUniform:
                    VideoElement.Stretch = Stretch.Uniform;
                    VideoElement.Height = ActualHeight;
                    VideoElement.Width = ActualWidth;
                    break;
                case Aspect.TVHeight:
                    VideoElement.Stretch = Stretch.Fill;
                    VideoElement.Width = ActualHeight * 4 / 3;
                    VideoElement.Height = ActualHeight;
                    break;
                case Aspect.TVWidth:
                    VideoElement.Stretch = Stretch.Fill;
                    VideoElement.Width = ActualWidth;
                    VideoElement.Height = ActualWidth * 3 / 4;
                    break;
                case Aspect.FillScreenStretch:
                    VideoElement.Stretch = Stretch.Fill;
                    VideoElement.Height = ActualHeight;
                    VideoElement.Width = ActualWidth;
                    break;
                case Aspect.FillScreen:
                    VideoElement.Stretch = Stretch.UniformToFill;
                    VideoElement.Height = ActualHeight;
                    VideoElement.Width = ActualWidth;
                    break;
            }
        }

        private Timer _stateTimer;
        private static bool _seekWarned;

        private void Timer_CallBack(object state)
        {
            Dispatcher.BeginInvoke(() =>
            { if (_mss != null) State.Text = _mss.GetStatistics() + Environment.NewLine + "Droped Frames: " + VideoElement.DroppedFramesPerSecond; });
        }

        private void StatisticToggle(object sender, RoutedEventArgs e)
        {
            var toggle = (ToggleButton)sender;


            if (toggle.IsChecked == true)
            {
                _stateTimer = new Timer(Timer_CallBack, null, 0, 1000);
                IIcon.Fill = new SolidColorBrush(Colors.Black);
            }
            else
            {
                if (_stateTimer != null)
                {
                    _stateTimer.Change(Timeout.Infinite, Timeout.Infinite);
                }
                State.Text = null;
                IIcon.Fill= new SolidColorBrush(Colors.White);
            }
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_serverAccess != null)
            {
                _serverAccess.Seek(e.NewValue);
            }
        }

        private void Border_Tap(object sender, GestureEventArgs e)
        {
            e.Handled = true;
            var fe = (FrameworkElement) sender;
            var pos = e.GetPosition(fe);
            var val = pos.X/fe.ActualWidth;
            if (_serverAccess != null)
            {
                _serverAccess.Seek(val*100);
            }
            if (!_seekWarned)
            {
                _seekWarned = true;
                MessageBox.Show(Res.SeekHint);
            }
        }

        private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
        }
    }
}
